package viper.receiver;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;

import viper.main.StateManager;
import viper.main.ViperHeader;
import viper.sender.UDPSender;

/**
 * @author Kevin Sin
 * 
 * Revision number: 3
 * Date created: 3/21/10
 * Date of Last Back-up on SVN: 3/21/10
 * 
 * <p>This class is a low-level class that just parses
 * the packets received from UDPReceiver.  If it is a call 
 * receive packet, use CallReceiver.INSTANCE.parsePacket().
 */
public class UDPReceiverWorker implements Runnable {
	
	public DatagramPacket packet;
	private byte[] bytes;
	private byte[] temp;

	public UDPReceiverWorker(DatagramPacket packet)
	{
		this.packet = packet;
	}
	
	@Override
	public void run() {
		bytes = packet.getData();
		
		switch(bytes[0]) {
			case ViperHeader.RELAY:
				handleRelayPacket();
				break;
			case ViperHeader.STATISTICS:
				handleStatisticPacket();
				break;
			case ViperHeader.RECEIVE: //handle receive packet
				StateManager.INSTANCE.getCallReceiver().parsePacket(
						ViperHeader.getPayloadFromReceivePacket(bytes)
						); 
				break;
			case ViperHeader.CALL_SETUP: 
				String sourceAddrIP = packet.getAddress().getHostAddress();
				handleCallSetupPacket(sourceAddrIP);
				break;
			default: 
				System.out.println("ignore"); 
				break;
		} 
	}
	
	private void handleCallSetupPacket(String sourceAddrIP)
	{
		switch(bytes[1]) {
			case ViperHeader.SETUP_MAKE_CALL: //somebody wants to talk
				//extract type of code and relay info
				byte[] payload = ViperHeader.getPayloadFromSetupPacket(bytes);
				byte typeCode = payload[0];
				int numRelays=0;
				if(typeCode==ViperHeader.CODE_111)
					numRelays = 1;
				else if(typeCode==ViperHeader.CODE_232)
					numRelays=3;
				else if(typeCode==ViperHeader.CODE_364)
					numRelays=6;
				String[] relays = new String[numRelays];
				for(int i=0; i<numRelays; i++)
				{
					byte[] ipBytes = new byte[]{
							payload[4*i + 1],
							payload[4*i + 2],
							payload[4*i + 3],
							payload[4*i + 4]
						};
					relays[i] = 
						UDPSender.ipBytesToInetAddress(ipBytes).getHostAddress();
				}
				StateManager.INSTANCE.gotCall(sourceAddrIP, typeCode, relays);
				break;
			case ViperHeader.SETUP_ACCEPT_CALL: //person accepted your call
				StateManager.INSTANCE.callAccepted();
				break;
			case ViperHeader.SETUP_REJECT_CALL: //person rejected your call
				StateManager.INSTANCE.callRejected();
				break;
			case ViperHeader.SETUP_ERROR: //handle error
				break;
			case ViperHeader.SETUP_CANCEL_CALL: //call cancelled
				StateManager.INSTANCE.callCancelled();
				break;
			case ViperHeader.SETUP_END_CALL: //call ended
				StateManager.INSTANCE.callEnded();
				break;
			default: 
				System.out.println("ignored call setup packet"); 
				break; 
		}
	}
	
	/* 
	*/ 
	/**
	 * At a relay, upon receiving a packet from the Sender, 
	 * the relay changes the VIPER header
	 * of the packet and forwards it to the sender.
	 * 
	 * Format of a relay packet
	 * <Relay (1 byte)> <RX IP Addr (4 bytes)> <Seq # (1 byte)> <Pkt Type (1 byte)> <Data>
	 */
	private void handleRelayPacket() {
		temp = new byte[bytes.length - 4]; //temporary byte[] to store the payload
		temp[0] = ViperHeader.RECEIVE;
		System.arraycopy(bytes, 5, temp, 1, bytes.length - 5);
		packet.setData(temp);
		byte[] ipByte = new byte[4];
		System.arraycopy(bytes, 1, ipByte, 0, 4);
		InetAddress rxAddr = null;
		try {
			rxAddr = InetAddress.getByAddress(ipByte);
			packet.setAddress(rxAddr);
			StateManager.INSTANCE.getDatagramSocket().send(packet);
			
		} catch (UnknownHostException e1) {
			e1.printStackTrace();
		}catch (SocketException e2) {
			e2.printStackTrace();
		} catch (IOException e3) {
			e3.printStackTrace();
		}
	}
	private void handleStatisticPacket() {
		temp = new byte[bytes.length - 1]; //temporary byte[] to store the payload
		System.arraycopy(bytes, 1, temp, 0,bytes.length-1);
		long [] templong = new long[temp.length];
		templong[0] = (bytes[1] << 56)
        + ((bytes[2] & 0xFF) << 48)
        + ((bytes[3] & 0xFF) << 40)
        + ((bytes[4] & 0xFF) << 32)
        + ((bytes[5] & 0xFF) << 24)
        + ((bytes[6] & 0xFF) << 16)
        + ((bytes[7] & 0xFF) << 8)
        + (bytes[8] & 0xFF);
		
		templong[1] = (bytes[9] << 56)
        + ((bytes[10] & 0xFF) << 48)
        + ((bytes[11] & 0xFF) << 40)
        + ((bytes[12] & 0xFF) << 32)
        + ((bytes[13] & 0xFF) << 24)
        + ((bytes[14] & 0xFF) << 16)
        + ((bytes[15] & 0xFF) << 8)
        + (bytes[16] & 0xFF);
		//CONVERTTHESE TO LONGS
		StateManager.INSTANCE.getCallReceiver().setPacketsSent(templong);
		}
}
